package com.kzj.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.kzj.dao.AcademyDao;
import com.kzj.dao.CourseSelectionInformationDao;
import com.kzj.dao.MajorDao;
import com.kzj.domain.*;
import com.kzj.dao.StudentDao;
import com.kzj.domain.dto.ChangePasswordDTO;
import com.kzj.domain.dto.UserLoginDTO;
import com.kzj.domain.vo.*;
import com.kzj.global.constant.GlobalConstant;
import com.kzj.global.exception.MyException;
import com.kzj.global.params.GlobalParams;
import com.kzj.global.re.Result;
import com.kzj.global.threadLocal.UserThreadLocal;
import com.kzj.global.utils.EncodeUtil;
import com.kzj.global.utils.GlobalUtil;
import com.kzj.global.utils.IdGeneratorUtil;
import com.kzj.global.utils.MatchesUtil;
import com.kzj.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.swagger.models.auth.In;
import jodd.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.User;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.connection.stream.*;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestBody;

import javax.annotation.PostConstruct;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author kzj
 * @since 2023-12-01
 */
@Service
@Slf4j
public class StudentServiceImpl extends ServiceImpl<StudentDao, Student> implements StudentService {
    @Autowired
    StudentDao studentDao;
    @Autowired
    StringRedisTemplate stringRedisTemplate;
    @Autowired
    MajorDao majorDao;
    @Autowired
    AcademyDao academyDao;
    @Autowired
    CourseSelectionInformationDao courseSelectionInformationDao;
    @Autowired
    EletiveCourseService eletiveCourseService;
    @Autowired
    StudentService proxy;
    @Autowired
    CourseSelectionInformationService courseSelectionInformationService;
    @Autowired
    StudentExaminationInformationService studentExaminationInformationService;
    @Autowired
    ExaminationService examinationService;
    @Autowired
    ScoreService scoreService;
    @Autowired
    ClassroomService classroomService;
    @Autowired
    StudentClassService studentClassService;
    @Autowired
    GlobalParams globalParams;
    private static final DefaultRedisScript<Long> redisScript=new DefaultRedisScript<>();;
    private static final ExecutorService robClassesThread= Executors.newSingleThreadExecutor();
    static{
        redisScript.setLocation(new ClassPathResource(GlobalConstant.ROB_CLASSES_LUA_SCRIPT));
        redisScript.setResultType(Long.class);
    }
    @PostConstruct
    void inti()
    {
        try{
//            stringRedisTemplate.execute((RedisCallback<? extends Object>) redisConnection -> {
//                redisConnection.xGroupCreate(
//                        GlobalConstant.ROB_CLASSES_STREAM.getBytes(),
//                        GlobalConstant.ROB_CLASSES_GROUP,
//                        ReadOffset.lastConsumed(),
//                        true
//                );
//                return null;
//            });
        }catch (Exception e){
            throw new RuntimeException(e);
//            log.error(GlobalConstant.GROUP_CREATE_ERROR);
        }
        robClassesThread.submit(new RobClassesHandlerRedisStream());
    }
    public class RobClassesHandlerRedisStream implements Runnable{
        @Override
        public void run() {
            while(true)
            {
                try{
                    List<MapRecord<String, Object, Object>> read = stringRedisTemplate.opsForStream().read(
                            Consumer.from(GlobalConstant.ROB_CLASSES_GROUP, GlobalConstant.ROB_CLASSES_USER),
                            StreamReadOptions.empty().count(1),
                            StreamOffset.create(GlobalConstant.ROB_CLASSES_STREAM, ReadOffset.lastConsumed())
                    );
                    if(read==null||read.isEmpty())
                    {
                        continue;
                    }
                    MapRecord<String, Object, Object> entries = read.get(0);
                    Map<Object, Object> value = entries.getValue();
                    CourseSelectionInformation courseSelectionInformation = BeanUtil.fillBeanWithMap(value,
                            new CourseSelectionInformation(), true);
                    courseSelectionInformation.setSelectTime(LocalDateTime.now());
                    proxy.handlerCourseSelectionInformation(courseSelectionInformation);
                    stringRedisTemplate.opsForStream().acknowledge(GlobalConstant.ROB_CLASSES_STREAM,
                            GlobalConstant.ROB_CLASSES_GROUP,entries.getId());
                }catch (Exception e){
                    log.warn(GlobalConstant.ROB_CLASSES_INFO_HANDLE_EXCEPTION);
                    handlerException();
                }
        }
    }
    }

    void handlerException()
    {
        try{
            List<MapRecord<String, Object, Object>> read = stringRedisTemplate.opsForStream().read(
                    Consumer.from(GlobalConstant.ROB_CLASSES_GROUP, GlobalConstant.ROB_CLASSES_USER),
                    StreamReadOptions.empty().count(1),
                    StreamOffset.create(GlobalConstant.ROB_CLASSES_STREAM, ReadOffset.from("0")));
            if(read==null||read.isEmpty())
            {
                return;
            }
            MapRecord<String, Object, Object> entries = read.get(0);
            Map<Object, Object> value = entries.getValue();
            CourseSelectionInformation courseSelectionInformation = BeanUtil.fillBeanWithMap(
                    value, new CourseSelectionInformation(),true);
            proxy.handlerCourseSelectionInformation(courseSelectionInformation);
            stringRedisTemplate.opsForStream().acknowledge(GlobalConstant.ROB_CLASSES_STREAM,
                    GlobalConstant.ROB_CLASSES_GROUP,entries.getId());
        }catch (Exception e){
            log.warn(GlobalConstant.ROB_CLASSES_INFO_HANDLE_EXCEPTION_AGAIN);
            handlerException();
        }
    }
    @Override
    public void   handlerCourseSelectionInformation(CourseSelectionInformation courseSelectionInformation)
    {
        log.info(GlobalConstant.ROB_CLASSES_INFO_HANDLE_SUCCESS);
        eletiveCourseService.update().setSql("amount = amount - 1").
                eq("id",courseSelectionInformation.getElectiveCourseId()).update();
        courseSelectionInformationDao.insert(courseSelectionInformation);
    }

    @Override
    public Result getOptionalCourseList() {
        Student user = UserThreadLocal.getUser(Student.class);
        Integer nextTerm = GlobalUtil.getStudentTermByDateOfEnrollment(user.getDateOfEnrollment())+1;
        System.out.println(nextTerm);
        List<ElectiveCourseVO> generalElectiveCourseList = getGeneralElectiveCourseList(nextTerm);
        List<ElectiveCourseVO> professionalElectiveCourseList = getProfessionalElectiveCourseList(user.getMajorId(), nextTerm);
        professionalElectiveCourseList.addAll(generalElectiveCourseList);
        professionalElectiveCourseList.stream().forEach(item -> eletiveCourseService.addAmountByElectiveCourseVO(item));
        return Result.success(professionalElectiveCourseList);
    }



    public List<ElectiveCourseVO> getGeneralElectiveCourseList(Integer term)
    {
        String key0=GlobalConstant.MAJOR_OPTIONAL_COURSE_ID_PREFIX+GlobalConstant.COURSE_RESTRICTED_MAJORS_GENERAL_ELECTIVE_COURSE+GlobalConstant.MAJOR_OPTIONAL_COURSE_TERM_PREFIX+term;
        String s0 = stringRedisTemplate.opsForValue().get(key0);
        if(s0==null)
            return Collections.emptyList();
        String[] ids = s0.split(",");
        return (List<ElectiveCourseVO>)eletiveCourseService.getElectiveCourseVOByIds(Arrays.asList(ids)).getData();
    }
    public List<ElectiveCourseVO> getProfessionalElectiveCourseList(String majorId, Integer term)
    {
        String key=GlobalConstant.MAJOR_OPTIONAL_COURSE_ID_PREFIX+majorId+GlobalConstant.MAJOR_OPTIONAL_COURSE_TERM_PREFIX+term;
        String s = stringRedisTemplate.opsForValue().get(key);
        if(s==null)
            return Collections.emptyList();
        String[] ids = s.split(",");
        return (List<ElectiveCourseVO>)eletiveCourseService.getElectiveCourseVOByIds(Arrays.asList(ids)).getData();
    }

    @Override
    public Result updateStudent(Student student) {
        MatchesUtil.idMatches(student.getId());
        MatchesUtil.objMatches(student);
        String key=EncodeUtil.encode(student.getId(),GlobalConstant.STUDENT_INFO_PREFIX);
        synchronized (student.getId())
        {
            this.update(student);
            Student s = this.getById(student.getId());
            UserLoginVO userLoginVO=new UserLoginVO();
            userLoginVO.setObj(s);
            userLoginVO.setType(GlobalConstant.STUDENT_TYPE);
            String jsonString = JSONObject.toJSONString(userLoginVO);
            stringRedisTemplate.opsForValue().set(key,jsonString,GlobalConstant.SCHEDULE_TTL,TimeUnit.MINUTES);
        }
        return Result.success();
    }

    @Override
    public Result cancelCourse(String courseId) {
        MatchesUtil.idMatches(courseId);
        Student user = UserThreadLocal.getUser(Student.class);
        Integer nextTerm = GlobalUtil.getStudentTermByDateOfEnrollment(user.getDateOfEnrollment())+1;
        String key=GlobalConstant.SCHEDULE_STUDENT_ID_PREFIX+user.getId()+GlobalConstant.SCHEDULE_TERM_PREFIX+nextTerm;
        String s = stringRedisTemplate.opsForValue().get(key);
        if(s==null||!s.contains(courseId))
        {
            throw new MyException(GlobalConstant.UNKNOWN_COURSE);
        }
        String[] split = s.split(",");
        List<String> list=new ArrayList<>();
        for (String string : split) {
            if(!string.equals(courseId))
                list.add(string);
        }
        String join = StringUtil.join(list, ",");
        stringRedisTemplate.opsForValue().set(key,join,GlobalConstant.SCHEDULE_TTL,TimeUnit.MINUTES);
        String amountKey=GlobalConstant.COURSE_AMOUNT_PREFIX+courseId;
        stringRedisTemplate.opsForValue().increment(amountKey);
        stringRedisTemplate.opsForSet().remove(GlobalConstant.COURSE_ID_PREFIX+courseId,user.getId());
        String keyNextTerm = GlobalConstant.ROB_CLASSES_NEXT_TERM_PREFIX + user.getId();
        String valueNextTerm = stringRedisTemplate.opsForValue().get(keyNextTerm);
        String value = GlobalUtil.valueRemoveId(valueNextTerm, courseId);
        System.out.println(value);
        stringRedisTemplate.opsForValue().set(keyNextTerm,value);
        LambdaQueryWrapper<CourseSelectionInformation> lqw=new LambdaQueryWrapper<>();
        lqw.eq(CourseSelectionInformation::getStudentId,user.getId())
                        .eq(CourseSelectionInformation::getElectiveCourseId,courseId);
        courseSelectionInformationDao.delete(lqw);
        return Result.success(GlobalConstant.CANCEL_COURSE_SUCCESS);
    }

    @Override
    public Result getCancelCourseList() {
        Student user = UserThreadLocal.getUser(Student.class);
        Integer nextTerm = GlobalUtil.getStudentTermByDateOfEnrollment(user.getDateOfEnrollment())+1;
        String key=GlobalConstant.SCHEDULE_STUDENT_ID_PREFIX+user.getId()+GlobalConstant.SCHEDULE_TERM_PREFIX+nextTerm;
        String s = stringRedisTemplate.opsForValue().get(key);
        if(s==null)
        {
            return Result.success(Collections.emptyList());
        }
        String[] split = s.split(",");
        List<ElectiveCourseVO> electiveCourseVOS=(List<ElectiveCourseVO>)eletiveCourseService.getElectiveCourseVOByIds(Arrays.asList(split)).getData();
        electiveCourseVOS.stream().forEach(new java.util.function.Consumer<ElectiveCourseVO>() {
            @Override
            public void accept(ElectiveCourseVO electiveCourseVO) {
                String key=GlobalConstant.COURSE_AMOUNT_PREFIX+electiveCourseVO.getId();
                String s1 = stringRedisTemplate.opsForValue().get(key);
                electiveCourseVO.setAmount(Integer.valueOf(s1));
            }
        });
        return Result.success(electiveCourseVOS);
    }

    @Override
    public Result getCurrTermExamination(String studentId) {
        Student student = this.getById(studentId);
        Integer term=GlobalUtil.getStudentTermByDateOfEnrollment(student.getDateOfEnrollment());
        return Result.success(getExaminationByStudentAndTerm(studentId,term));
    }

    @Override
    public Result getScoreList(Integer term) {
        Student user=UserThreadLocal.getUser(Student.class);
        Integer nowTerm = GlobalUtil.getStudentTermByDateOfEnrollment(user.getDateOfEnrollment());
        CourseScoreStudentVO courseScoreStudentVO = new CourseScoreStudentVO();
        Integer dif = term - nowTerm;
        String tagTerm = GlobalUtil.getTagTerm(dif);
        courseScoreStudentVO.setTerm(GlobalUtil.getStringTerm(tagTerm));
        courseScoreStudentVO.setGrade(Collections.emptyList());
        if(nowTerm<term)
        {
            return Result.success(courseScoreStudentVO);
        }
        List<String> electiveCourseIds = courseSelectionInformationService.query()
                .eq("student_id", user.getId()).eq("select_term", term).list()
                .stream().map(item -> item.getElectiveCourseId()).collect(Collectors.toList());
        if(electiveCourseIds.isEmpty())
        {
            return Result.success(courseScoreStudentVO);
        }
        List<ElectiveCourseVO> electiveCourseVOS = eletiveCourseService.query().in("id", electiveCourseIds)
                .last("order by field(" + GlobalUtil.getHandlerList(electiveCourseIds) + ")").list()
                .stream().map(item -> eletiveCourseService.getElectiveCourseVO(item)).collect(Collectors.toList());
        List<ScoreByCourseVO> list=new ArrayList<>(electiveCourseVOS.size());
        for (ElectiveCourseVO electiveCourseVO : electiveCourseVOS) {
            ScoreByCourseVO scoreByCourseVO = new ScoreByCourseVO();
            scoreByCourseVO.setTeacher(electiveCourseVO.getTeacherName());
            scoreByCourseVO.setLesson(electiveCourseVO.getCourseName());
            scoreByCourseVO.setNature(electiveCourseVO.getType());
            scoreByCourseVO.setNumber(electiveCourseVO.getId());
            list.add(scoreByCourseVO);
        }
        courseScoreStudentVO.setGrade(list);
        List<String> examinationIds = examinationService.query().in("elective_course_id", electiveCourseIds)
                .last("order by field(elective_course_id," + GlobalUtil.getHandlerList(electiveCourseIds) + ")").list()
                .stream().map(item -> item.getId()).collect(Collectors.toList());
        if(examinationIds.isEmpty())
        {
            return Result.success(courseScoreStudentVO);
        }
        List<Score> scores = scoreService.query().eq("student_id", user.getId())
                .in("examination_id", examinationIds)
                .last("order by field(" + GlobalUtil.getHandlerList(examinationIds) + ")").list();
        if(scores.isEmpty())
        {
            return Result.success(courseScoreStudentVO);
        }
        Iterator<Score> iterator = scores.iterator();
        Score next = iterator.next();
        for(int i=0;i<examinationIds.size();i++)
        {
            if(next.getExaminationId().equals(examinationIds.get(i)))
            {
                list.get(i).setGrade(next.getGrade());
                if(iterator.hasNext())
                {
                    next=iterator.next();
                }
                else{
                    break;
                }
            }
        }
        return Result.success(courseScoreStudentVO);
    }

    public List<ExaminationVO> getExaminationByStudentAndTerm(String studentId,Integer term)
    {
        List<CourseSelectionInformation> list = courseSelectionInformationService.query().eq("student_id", studentId).eq("select_term", term).list();
        if(list.isEmpty())
            return Collections.emptyList();
        List<String> collect = list.stream().map(item -> item.getElectiveCourseId()).collect(Collectors.toList());
        List<ElectiveCourseVO> electiveCourseVOS = (List<ElectiveCourseVO>)eletiveCourseService.getElectiveCourseVOByIds(collect).getData();
        List<String> electiveCourseIds = electiveCourseVOS.stream().map(item -> item.getId()).collect(Collectors.toList());
        List<Examination> examinations = examinationService.query().in("elective_course_id", electiveCourseIds).last("order by field(elective_course_id," + GlobalUtil.getHandlerList(electiveCourseIds) + ")").list();
        List<String> examinationIds = examinations.stream().map(item -> item.getId()).collect(Collectors.toList());
        List<Integer> candidateNumbers = studentExaminationInformationService.query().eq("student_id", studentId).in("examination_id", examinationIds).last("order by field(examination_id," + GlobalUtil.getHandlerList(examinationIds) + ")").list().stream().map(item->item.getCandidateNumber()).collect(Collectors.toList());
        List<String> classroomIds = examinations.stream().map(item -> item.getClassroomId()).collect(Collectors.toList());
        List<Classroom> classrooms = classroomService.query().in("id", classroomIds).list();
        Map<String,String> map=new HashMap<>();
        for (Classroom classroom : classrooms) {
            map.put(classroom.getId(),classroom.getName());
        }
        List<ExaminationVO> examinationVOS=new ArrayList<>();
        for (int i = 0; i < electiveCourseIds.size(); i++) {
            ExaminationVO examinationVO = new ExaminationVO();
            String[] split = examinations.get(i).getStartTime().split(GlobalConstant.COURSE_SCHOOL_TIME_SPLIT);
            examinationVO.setCandidateNumber(candidateNumbers.get(i));
            examinationVO.setWeek(split[0]);
            examinationVO.setDayOfWeek(split[1]);
            examinationVO.setTimeOfDay(split[2]);
            examinationVO.setCourseName(electiveCourseVOS.get(i).getCourseName());
            examinationVO.setClassroomName(map.get(examinations.get(i).getClassroomId()));
            examinationVOS.add(examinationVO);
        }
        return examinationVOS;
    }


    @Override
    public Result login(UserLoginDTO userLoginDTO) {
        LambdaQueryWrapper<Student> lqw=new LambdaQueryWrapper<>();
        lqw.eq(Student::getId,userLoginDTO.getId())
                .eq(Student::getPassword,userLoginDTO.getPassword());
        Student student = studentDao.selectOne(lqw);
        if(student==null)
            throw new MyException(GlobalConstant.ID_OR_PASSWORD_ERROR);
        if(student.getStatus().equals(GlobalConstant.STATUS_UNUSABLE))
            throw new MyException(GlobalConstant.ACCOUNT_DISABLED);
        student.setPassword(null);
        StudentVO studentVO=getStudentVO(student);
        String key= EncodeUtil.encode(student.getId(),GlobalConstant.STUDENT_INFO_PREFIX);
        UserLoginVO userLoginVO = new UserLoginVO();
        userLoginVO.setObj(studentVO);
        userLoginVO.setType(GlobalConstant.STUDENT_TYPE);
        userLoginVO.setToken(key);
        stringRedisTemplate.opsForValue().
                set(key, JSONObject.toJSONString(userLoginVO),
                        GlobalConstant.LOGIN_INFO_TTL, TimeUnit.MINUTES);
        return Result.success(userLoginVO);
    }
    @Override
    public Result update(Student student) {
        MatchesUtil.idMatches(student.getId(),Student.class);
        MatchesUtil.objMatches(student);
        student.setPassword(null);
        boolean b = studentDao.updateById(student)>0;
        if(b)
        {
            String key= EncodeUtil.encode(student.getId(),GlobalConstant.STUDENT_INFO_PREFIX);
            stringRedisTemplate.delete(key);
//            if(student.getId())
            return Result.success();
        }
        return Result.error(GlobalConstant.UPDATE_FAIL);
    }

    @Override
    public Result changePassword(ChangePasswordDTO changePasswordDTO) {
        MatchesUtil.passwordMatchers(changePasswordDTO.getNewPassword());
        MatchesUtil.passwordMatchers(changePasswordDTO.getOldPassword());
        StudentVO user = UserThreadLocal.getUser(StudentVO.class);
        String password = this.query().le("id", user.getId()).one().getPassword();
        if(!password.equals(EncodeUtil.encode(changePasswordDTO.getOldPassword())))
        {
            throw new MyException(GlobalConstant.PASSWORD_ERROR);
        }
        String encodeNewPassword=EncodeUtil.encode(changePasswordDTO.getNewPassword());
        this.update().eq("id",user.getId()).set("password",encodeNewPassword).update();
        return Result.success();
    }
    @Override
    public Result addStudent(Student student) {
        MatchesUtil.objMatches(student);
        String id= IdGeneratorUtil.generatorId(Student.class);
        student.setId(id);
        if(student.getPassword()==null)
        {
            String idNumber = student.getIdNumber();
            String password=idNumber.substring(idNumber.length()-GlobalConstant.PASSWORD_DEFAULT_LEN);
            student.setPassword(EncodeUtil.encode(password));
        }
        else student.setPassword(EncodeUtil.encode(student.getPassword()));
        student.setStatus(GlobalConstant.STATUS_USABLE);
        this.save(student);
        return Result.success();
    }

    @Override
    public Result getStudentById(String id) {
        Student student = studentDao.selectById(id);
        return Result.success(getStudentVO(student));
    }

    @Override
    public Result getStudentByStudentClassId(String studentClassId) {
        List<Student> students = this.query().eq("student_class_id", studentClassId).list();
        List<StudentVO> collect = students.stream().map(this::getStudentVO).collect(Collectors.toList());
        return Result.success(collect);
    }

    @Override
    public Result getStudentByMajorId(String majorId) {
        List<Student> students = this.query().eq("major_id", majorId).list();
        List<StudentVO> collect = students.stream().map(this::getStudentVO).collect(Collectors.toList());
        return Result.success(collect);
    }

    @Override
    public Result getStudentByAcademyId(String academyId) {
        LambdaQueryWrapper<Major> lqw=new LambdaQueryWrapper<>();
        lqw.eq(Major::getAcademyId,academyId);
        List<Major> majors = majorDao.selectList(lqw);
        List<String> majorIds = majors.stream().map(item -> item.getId()).collect(Collectors.toList());
        List<Student> students = this.query().in("major_id", majorIds).last("order by field(major_id," + GlobalUtil.getHandlerList(majorIds) + ")").list();
        Academy academy = academyDao.selectById(academyId);
        List<StudentVO> studentVOS=new ArrayList<>(students.size());
        int majorIndex=0;
        for (Student student : students) {
            StudentVO studentVO = new StudentVO();
            BeanUtils.copyProperties(student,studentVO);
            studentVO.setAcademy(academy.getName());
            while(!majors.get(majorIndex).getId().equals(student.getMajorId()))
            {
                majorIndex++;
            }
            studentVO.setMajor(majors.get(majorIndex).getName());
            studentVOS.add(studentVO);
        }
        return Result.success(studentVOS);
    }

    @Override
    public Result robClasses(String courseId) {
        LocalDateTime now = LocalDateTime.now();
        if(globalParams.getRobClassesPreHeatDate().isAfter(now)||globalParams.getStopRobClassesDate().isBefore(now))
            throw new MyException(GlobalConstant.TIME_UNUSUAL);
        Student student=UserThreadLocal.getUser(Student.class);
        Integer nextTerm = GlobalUtil.getStudentTermByDateOfEnrollment(student.getDateOfEnrollment())+1;
        String key=GlobalConstant.MAJOR_OPTIONAL_COURSE_ID_PREFIX+
                student.getMajorId()+
                GlobalConstant.MAJOR_OPTIONAL_COURSE_TERM_PREFIX+nextTerm;
        String key0=GlobalConstant.MAJOR_OPTIONAL_COURSE_ID_PREFIX+
                GlobalConstant.COURSE_RESTRICTED_MAJORS_GENERAL_ELECTIVE_COURSE+
                GlobalConstant.MAJOR_OPTIONAL_COURSE_TERM_PREFIX+nextTerm;
        String s = stringRedisTemplate.opsForValue().get(key);
        String s0 = stringRedisTemplate.opsForValue().get(key0);
        s+=","+s0;
        String[] split = s.split(",");
        List<String> courseIds= Arrays.asList(split);
        if(!courseIds.contains(courseId))
        {
            throw new MyException(GlobalConstant.NOT_ALLOW);
        }
        String id=student.getId();
        courseTimeConflictJudge(id,courseId);
        Long execute = stringRedisTemplate.execute(redisScript,
                Collections.emptyList(), courseId, id,nextTerm.toString());
        if(execute!=0)
        {
            return execute==1? Result.error(GlobalConstant.COURSE_QUANTITY_NOT_SUFFICIENT):
                    Result.error(GlobalConstant.PLEASE_DONT_STEAL_LESSONS_AGAIN);
        }
        addNextTermKey(id,courseId);
        addNextTermSchedule(id,nextTerm,courseId);
        if(proxy==null)
            proxy= (StudentService) AopContext.currentProxy();
        return Result.success();
    }
    public void addNextTermSchedule(String studentId,Integer term,String courseId)
    {
        String key=GlobalConstant.SCHEDULE_STUDENT_ID_PREFIX+studentId+GlobalConstant.SCHEDULE_TERM_PREFIX+term;
        String s = stringRedisTemplate.opsForValue().get(key);
        if(s==null)
        {
            s=courseId;
        }
        else{
            s+=","+courseId;
        }

        stringRedisTemplate.opsForValue().set(key,s,GlobalConstant.SCHEDULE_TTL, TimeUnit.DAYS);
    }
    public void addNextTermKey(String studentId,String courseId)
    {
        String nextTermKey= GlobalConstant.ROB_CLASSES_NEXT_TERM_PREFIX+studentId;
        String s1 = stringRedisTemplate.opsForValue().get(nextTermKey);
        if(s1==null)
        {
            stringRedisTemplate.opsForValue().set(nextTermKey,courseId+"");
        }
        else{
            stringRedisTemplate.opsForValue().set(nextTermKey,s1+","+courseId);
        }
    }
    public void courseTimeConflictJudge(String studentId,String courseId)
    {
        String nextTermKey= GlobalConstant.ROB_CLASSES_NEXT_TERM_PREFIX+studentId;
        String s1 = stringRedisTemplate.opsForValue().get(nextTermKey);
        if(s1==null||s1.equals(""))
        {
           return;
        }
        String s2 = stringRedisTemplate.opsForValue().get(GlobalConstant.COURSE_INFO_PREFIX + courseId);
        JSONObject jsonObject = (JSONObject)JSONObject.parse(s2);
        ElectiveCourseVO currCourse = jsonObject.toJavaObject(ElectiveCourseVO.class);
        String[] split1 = s1.split(",");
        for (String courseId2 : split1) {
            s2 = stringRedisTemplate.opsForValue().get(GlobalConstant.COURSE_INFO_PREFIX + courseId2);
            if(s2.equals(""))
                continue;
            jsonObject = (JSONObject)JSONObject.parse(s2);
            ElectiveCourseVO course = jsonObject.toJavaObject(ElectiveCourseVO.class);
            if(course.getCourseId().equals(currCourse.getCourseId()))
                throw new MyException(GlobalConstant.COURSE_TIME_CONFLICT);
            if(!course.getSchooltime().equals(currCourse.getSchooltime()))
                continue;
            if(course.getStartWeek()>currCourse.getEndWeek()||course.getEndWeek()<currCourse.getStartWeek())
                continue;
            throw new MyException(GlobalConstant.COURSE_TIME_CONFLICT);
        }

    }

    @Override
    public StudentVO getStudentVO(Student student)
    {
        String majorId = student.getMajorId();
        Major major = majorDao.selectById(majorId);
        Academy academy = academyDao.selectById(major.getAcademyId());
        StudentVO studentVO = new StudentVO();
        studentVO.setAcademyId(academy.getId());
        studentVO.setAcademy(academy.getName());
        studentVO.setMajor(major.getName());
        studentVO.setStudentClass(studentClassService.getClassNameBYStudentClassId(student.getStudentClassId()));
        BeanUtils.copyProperties(student,studentVO);
        return studentVO;
    }



}
